package com.njitzx.fileupload.service.impl;

import com.njitzx.fileupload.enu.FileCategory;
import com.njitzx.fileupload.enu.FileType;
import com.njitzx.fileupload.exception.ServiceException;
import com.njitzx.fileupload.mapper.FileMapper;
import com.njitzx.fileupload.pojo.FileEntity;
import com.njitzx.fileupload.service.FileService;
import com.njitzx.fileupload.util.MD5Util;
import com.njitzx.fileupload.util.MinioTemplate;
import io.minio.errors.MinioException;
import lombok.SneakyThrows;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Log
public class FileServiceImpl implements FileService {
    @Autowired
    private MinioTemplate minioTemplate;

    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;
    @Value("${minio.endpoint}")
    private String endPoint;
    @Value("${minio.bucketName}")
    private String bucketName;
    @Autowired
    private FileMapper fileMapper;

    //如果已经存在 直接返回对应的地址即可
    @SneakyThrows
    @Override
    public Map<String, Object> getMd5(String md5) {
        // 从数据库查询文件信息
        FileEntity file = fileMapper.getMd5(md5);

        // 如果文件不存在，直接返回 null
        if (file == null) {
            throw  new ServiceException("文件不存在");
        }

        // 获取文件相关信息
        String bucketName = file.getBuckName();
        String objectName = file.getObjectName();
        String fileUrl = file.getFileUrl();
        String fileType = file.getFileType();
        String fileName = file.getFileName();

        try {
            // 检查文件是否存在于 MinIO
            if (!minioTemplate.isExist(objectName)) {
                // 文件不存在
                return null;
            }
        } catch (MinioException e) {
            // 如果 MinIO 抛出异常，记录异常信息
            return null;
        }

        // 文件存在，构建返回的 Map
        Map<String, Object> result = new HashMap<>();
        result.put("url", fileUrl);
        result.put("fileType", fileType);
        result.put("fileName", fileName);

        return result;
    }


    //上传文件
    @Override
    public Map<String, Object> uploadV1(MultipartFile file) {
        //文件的名称
        String md5Name = MD5Util.getFileMD5(file);

        String fileName = file.getOriginalFilename();

        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
        String folderName = generateFolderName(fileSuffix); //文件夹名称

        String objectName = folderName + md5Name + fileSuffix;  //objectName

        String fileUrl = endPoint + "/" + bucketName + "/" + objectName; //完整的URL

        minioTemplate.pubObject(objectName, file);  //存入到minio中去

        FileEntity file1 = FileEntity.builder().fileName(fileName).fileType(fileSuffix.substring(1))
                .fileUrl(fileUrl).endpoint(endPoint).buckName(bucketName).objectName(objectName).createdAt(new Date()).updatedAt(new Date()).fileMd5(md5Name).build();
        //插入文件信息
        HashMap<String, Object> map = new HashMap<>();
        fileMapper.insertFile(file1);
        //存储map中返回给前端
        map.put("fileUrl", fileUrl);
        map.put("fileName", fileName);
        map.put("fileType", fileSuffix.substring(1));
        return map;
    }

    @SneakyThrows
    @Override
    public void uploadV2(Long fileID, Integer currentChunk, Integer totalChunk, MultipartFile file) {
        //当前的文件夹
        String currentDir = System.getProperty("user.dir");

        String tempDir = currentDir + "/upload/" + fileID;

        File dir = new File(tempDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        // 保存当前分片
        String chunkPath = tempDir + "/chunk_" + currentChunk;
        File chunkFile = new File(chunkPath);
        file.transferTo(chunkFile);
        // 记录当前上传分片，等待后续合并
        System.out.println("保存分片：" + chunkPath);
        // 如果所有分片上传完毕，返回合并请求


//    @Override
//    public Map<String, Object> uploadV2(FileEntity file) {
//        //文件的名称
//        String md5Name = MD5Util.getFileMD5(file);
//
//        String fileName = file.getOriginalFilename();
//        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
//        String folderName = generateFolderName(fileSuffix); //文件夹名称
//
//        String objectName = folderName + md5Name + fileSuffix;  //objectName
//        String fileUrl = endPoint + "/" + bucketName + "/" + objectName;
//
//        minioTemplate.pubObject(objectName, file);
//        FileEntity file1 = FileEntity.builder().fileName(fileName).fileType(fileSuffix.substring(1))
//                .fileUrl(fileUrl).endpoint(endPoint).buckName(bucketName).objectName(objectName).createdAt(new Date()).updatedAt(new Date()).fileMd5(md5Name).build();
//        //插入文件信息
//        HashMap<String, Object> map = new HashMap<>();
//        fileMapper.insertFile(file1);
//        map.put("fileUrl", fileUrl);
//        map.put("fileName", fileName);
//        return map;
//    }
    }

    @SneakyThrows
    @Override
    public Map<String, Object> merge(Long fileID, String fileType, String fileName) {
        String currentDir = System.getProperty("user.dir");
        String type = fileType.substring(fileType.lastIndexOf("/") + 1);
        String tempDir = currentDir + "/upload/" + fileID;
        File dir = new File(tempDir);

        if (!dir.exists()) {
            throw new ServiceException("分片文件不存在");
        }

        // 获取所有的分片文件
        List<File> chunkFiles = Arrays.asList(Objects.requireNonNull(dir.listFiles()));

        // 按照分片编号排序
        chunkFiles.sort(Comparator.comparingInt(file -> Integer.parseInt(file.getName().split("_")[1])));

        // 合并所有分片文件
        File outputFile = new File(currentDir + "/upload/" + fileID + "." + type);
        try (FileOutputStream outputStream = new FileOutputStream(outputFile, true)) {
            //遍历所有的小的文件
            for (File chunk : chunkFiles) {
                try (FileInputStream inputStream = new FileInputStream(chunk)) {
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, len);
                    }
                }
            }
        }
        //将木桶的文件 转换成 MultipartFile
        MultipartFile multipartFile = new MockMultipartFile(
                "file",                           // 表单中文件参数的名字
                fileName,             // 文件名
                fileType,                // 文件类型
                new FileInputStream(outputFile)   // 文件输入流
        );
        // 调用 fileService.uploadV1 方法上传文件
        Map<String, Object> map = uploadV1(multipartFile);
        // 返回合并后的文件URL
        return map;
    }

    private String generateFolderName(String fileSuffix) {
        String fileBucket = getFileBucket(fileSuffix);
        if (fileBucket == null) {
            return null;
        }
        // 获取当前日期
        Date now = new Date();
        // 使用 SimpleDateFormat 来格式化日期为 "yyyy/MM/dd" 格式
        DateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String date = sdf.format(now);
        // 组合最终的文件夹路径：如 "image/2024/11/30/"
        return fileBucket + date + "/";
    }

    private static String getFileBucket(String fileSuffix) {
        return FileCategory.getBucketPrefixByExtension(fileSuffix);
    }

    // 通过枚举类校验文件的大小
    private void preCheck(MultipartFile file) {
        String fileName = file.getOriginalFilename();  // 得到原来的名称
        String fileSuffix = fileName.substring(fileName.lastIndexOf(".")); // 拿到后缀
        FileType fileType = FileType.getFileTypeByExtension(fileSuffix);
        if (fileType == null) {
            throw new ServiceException("不支持的文件格式");
        }

        // 校验文件大小
        if (file.getSize() > fileType.getMaxSize()) {
            throw new ServiceException("文件大小超过" + (fileType.getMaxSize() / (1024 * 1024)) + "MB");
        }
    }
}
